package com.aloda.aop;

import com.aloda.annotation.RateLimit;
import com.aloda.constant.ErrorCode;
import com.aloda.constant.RedisConstant;
import com.aloda.context.UserBaseContext;
import com.aloda.exception.BaseException;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Slf4j
@Aspect
@Component
public class RateLimitAspect {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private ResourceLoader resourceLoader;

    private String luaScript; // 用于存储 Lua 脚本内容

    /**
     * 初始化方法：加载 Lua 脚本
     */
    @PostConstruct
    public void init() {
        try {
            // 加载 Lua 脚本文件
            org.springframework.core.io.Resource resource = resourceLoader.getResource("classpath:lua-scripts/rate_limit.lua");
            luaScript = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new RuntimeException("Failed to load Lua script", e);
        }
    }

    /**
     * 对方法进行限流操作
     *
     * @param joinPoint 切点
     * @param rateLimit 注解
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        // 获取当前用户的唯一标识
        Long userId = UserBaseContext.getUser().getId();
        if (userId == null) {
            throw new BaseException(ErrorCode.SYSTEM_ERROR);
        }

        // 构造 Redis 键
        String key = RedisConstant.RATE_LIMIT_KEY + userId + ":" + joinPoint.getSignature().getName();

        // 执行 Lua 脚本
        List<String> keys = Collections.singletonList(key);
        List<String> args = Arrays.asList(String.valueOf(rateLimit.limit()), String.valueOf(rateLimit.period()));
        Long remainingTime = stringRedisTemplate.execute(
                new DefaultRedisScript<>(luaScript, Long.class),
                keys,
                args.toArray()
        );

        // 判断是否超过限流
        if (remainingTime != null && remainingTime > 0) {
            String message = String.format("操作过于频繁，请稍后再试。剩余重置时间：%d 秒", remainingTime);
            throw new BaseException(message);
        }

        // 继续执行方法
        return joinPoint.proceed();
    }
}